/* lfstruncate.c  -  lfstruncate */

#include <xinu.h>

/*------------------------------------------------------------------------
 * lfstruncate  -  truncate a file by freeing its index and data blocks
 *			(assumes directory mutex held)
 *------------------------------------------------------------------------
 */
status	lfstruncate (
	  char *name				/* file name to truncate		*/
	)
{
	char	*nam, *cmp;			/* ptrs used during comparison	*/
	int32	i;					/* general loop index			*/
	struct	lflcblk	*lfptr;		/* ptr to open file table entry	*/
	struct	lfdir	*dirptr;	/* ptr to in-memory directory	*/
	bool8	found;				/* was the name found?			*/
	int32	retval;				/* value returned from function	*/
	struct	ldentry	*ldptr;		/* ptr to an entry in directory	*/
	ibid32	ifree;				/* start of index blk free list	*/
	ibid32	firstib;			/* first index blk of the file	*/
	ibid32	nextib;				/* 	 walks down list of the		*/
								/*    file's index blocks		*/
	dbid32	nextdb;				/* next data block to free		*/
	struct	lfiblk	iblock;		/* buffer for one index block	*/						
	
	/* Check length of name file (leaving space for NULLCH 	*/
	nam = name;
	for (i=0; i< LF_NAME_LEN; i++) {
		if (*nam++ == NULLCH) {
			break;
		}
	}
	if (i >= LF_NAME_LEN) {		/* name is too long */
		kprintf("Name is too long\r\n");
		return SYSERR;
	}
	
	/* Check if named file is open */
	for (i=0; i<Nlfl; i++) {	/* search file pseudo-devices	*/
		lfptr = &lfltab[i];
		if (lfptr->lfstate == LF_USED) {
			/* Compare requested name to name of open file 		*/
			nam = name;
			cmp = lfptr->lfname;
			while(*nam != NULLCH) {
				if (*nam != *cmp) {
				break;
				}
				nam++;
				cmp++;
			}
			/* if named file is open, call lftruncate directly	*/
			if ( (*nam==NULLCH) && (*cmp == NULLCH) ) {
				kprintf("file to truncate is open\r\n");
				wait(lfptr->lfmutex);
				retval = lftruncate(lfptr);
				signal(lfptr->lfmutex);
				kprintf("truncated the open file\r\n");
				return retval;
			}
		}
	}
	
	/* Named file is not open	*/
	/* Obtain copy of directory if not already present in memory	*/
	dirptr = &Lf_data.lf_dir;
	if (! Lf_data.lf_dirpresent) {
	    retval = read(Lf_data.lf_dskdev,(char *)dirptr,LF_AREA_DIR);
	    if (retval == SYSERR ) {
		kprintf("Cannot not read directory into memory\r\n");
		return SYSERR;
	    }
	    Lf_data.lf_dirpresent = TRUE;
	}
	
	/* Search directory to see if file exists */
	found = FALSE;
	for (i=0; i<dirptr->lfd_nfiles; i++) {
		ldptr = &dirptr->lfd_files[i];
		nam = name;
		cmp = ldptr->ld_name;
		while(*nam != NULLCH) {
			if (*nam != *cmp) {
				break;
			}
			nam++;
			cmp++;
		}
		if ( (*nam==NULLCH) && (*cmp==NULLCH) ) { /* name found	*/
			found = TRUE;
			break;
		}
	}
	
	/* if file does not exist, return SYSERR	*/
	if (! found) {
		signal(Lf_data.lf_mutex);
		kprintf("Named file to truncate does not exist\r\n");
		return SYSERR;
	}
	/* if file is in directory, free its index and data blocks	*/
	else {
		if (ldptr->ld_size == 0) {	/* file is already empty */
		return OK;
		}
		/* Obtain ID of first index block on free list */
		ifree = Lf_data.lf_dir.lfd_ifree;
		
		/* Record file's first i-block and clear directory entry */
		firstib = ldptr->ld_ilist;
		ldptr->ld_ilist = LF_INULL;
		ldptr->ld_size = 0;
		Lf_data.lf_dirdirty = TRUE;
		
		/* Walk along index block list, disposing of each data block*/
		/*  and clearing the corresponding pointer.  A note on loop	*/
		/*  termination: last pointer is set to ifree below.		*/
		for (nextib=firstib; nextib!=ifree; nextib=iblock.ib_next) {
			
			/* Obtain a copy of current index block from disk	*/
			lfibget(Lf_data.lf_dskdev, nextib, &iblock);
			
			/* Free each data block in the index block		*/
			for (i=0; i<LF_IBLEN; i++) {	/* for each d-block	*/
				/* Free the data block */
				nextdb = iblock.ib_dba[i];
				if (nextdb != LF_DNULL) {
					lfdbfree(Lf_data.lf_dskdev, nextdb);
				}

				/* Clear entry in i-block for this d-block	*/
					iblock.ib_dba[i] = LF_DNULL;
			}

			/* Clear offset (just to make debugging easier)		*/
			iblock.ib_offset = 0;

			/* For the last index block on the list, make it point	*/
			/*	to the current free list			*/
			if (iblock.ib_next == LF_INULL) {
				iblock.ib_next = ifree;
			}

			/* Write cleared i-block back to disk */
			lfibput(Lf_data.lf_dskdev, nextib, &iblock);
		}
		/* Last index block on the file list now points to first node	*/
		/*   on the current free list.  Once we make the free list	*/
		/*   point to the first index block on the file list, the	*/
		/*   entire set of index blocks will be on the free list	*/
		Lf_data.lf_dir.lfd_ifree = firstib;

		/* Indicate that directory has changed and return */
		Lf_data.lf_dirdirty = TRUE;
		
		return OK;
	}
}
